package com.chinasofti.framework.data;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.chinasofti.framework.base.Utils;
import com.chinasofti.framework.dataaccess.Condition.Operation;
import com.chinasofti.framework.parser.XmlParser;

public abstract class Entity {
	
	
	private HashMap<String, Object> m_values;
	private StringBuilder m_xmlString;
	
	protected abstract void init();
	public abstract XmlParser<? extends Entity> getParser();
	public abstract HashMap<String, FieldType> getFieldTypes();
	public abstract String getKeyField();
	public abstract String getEntityName();
	public Entity() {
		init();
		m_values = new HashMap<String, Object>();
	}

	public Type getFieldType(String name){
		HashMap<String, FieldType> fields = getFieldTypes();
		if(fields.get(name) == null) {
			return null;
		}
		return fields.get(name).getDataType();
	}
	
	public List<String> getFieldNameList() {
		HashMap<String, FieldType> fields = getFieldTypes();
		ArrayList<String> names = new ArrayList<String>();
		Iterator<?> iterator = fields.entrySet().iterator();
		while(iterator.hasNext()) {
			@SuppressWarnings("unchecked")
			Map.Entry<String, FieldType> entity = (Map.Entry<String, FieldType>)
					iterator.next();
			String name = entity.getKey();
			names.add(name);
		}
		return names;
	}
	
	public boolean isFieldSingle(String name) {
		HashMap<String, FieldType> fields = getFieldTypes();
		if(fields.get(name) == null) {
			return false;
		}
		return fields.get(name).isFieldSingle();
	}
	
	public boolean hasField(String name){
		HashMap<String, FieldType> fields = getFieldTypes();
		FieldType fieldType = fields.get(name);
		return (fieldType != null);
	}
	
	public Object getKey() {
		return get(getKeyField());
	}
	
	public Object get(String field){
		if(!hasField(field)) {
			return null;
		}
		Object obj = m_values.get(field);
		return obj;
	}

	public boolean set(String field, Object value) {
		if(!hasField(field)) {
			return false;
		}
		m_xmlString = null;
		Type fieldType = getFieldType(field);
		boolean singleData = isFieldSingle(field);
		if(!singleData) {
			@SuppressWarnings("unchecked")
			ArrayList<Object> values = (ArrayList<Object>)value;
			if(values == null) {
				return false;
			}
			else {
				boolean result = true;
				for(int i = 0; i < values.size(); i++) {
					Object element = values.get(i);
					if(!Utils.instanceOf(fieldType, element)) {
						result = false;
						break;
					}
				}
				if(!result) {
					return false;
				}
				else {
					m_values.put(field, values);
					return true;
				}
			}
		}
		else if (!Utils.instanceOf(fieldType, value)) {
			return false;
		}
		else {
			m_values.put(field, value);
			return true;
		}
	}
	
	@SuppressWarnings("unchecked")
	public <T extends Entity> ArrayList<T> getChildEntities(String field) {
		if(!hasField(field) || isFieldSingle(field)) {
			return null;
		}
		
		if(get(field) == null) {
			set(field, new ArrayList<T>());
		}
		return (ArrayList<T>)get(field);
	}
	
	public boolean compare(String fieldName, Object toValue, Operation oper) {
		Object value = get(fieldName);
		if(toValue == null || value == null) {
			if((toValue== null)== (value == null)) { 
					return Operation.EQUAL == oper;
			}
			else {
				return Operation.NOT_EQUAL == oper;
			}
		}
		Type fieldType = getFieldType(fieldName);
		
		if(fieldType.equals(Byte.class)
				|| fieldType.equals(Integer.class)
				|| fieldType.equals(Long.class)
				|| fieldType.equals(Short.class)) {
			long v = (long)getLong(fieldName);
			long v2 = Long.parseLong(toValue.toString()); 
			if(v == v2) {
				return Operation.EQUAL == oper;
			}
			else if(v > v2) {
				return (Operation.GREATER_THAN == oper || Operation.NOT_EQUAL == oper);
			}
			else {
				return (Operation.LESS_THAN == oper || Operation.NOT_EQUAL == oper);
			}
		}
		else if(fieldType.equals(Float.class)
				|| fieldType.equals(Double.class)) {	
			double v = (double)getLong(fieldName);
			double v2 = Double.parseDouble(toValue.toString());
			if(v == v2) {
				return Operation.EQUAL == oper;
			}
			else if(v > v2) {
				return (Operation.GREATER_THAN == oper || Operation.NOT_EQUAL == oper);
			}
			else {
				return (Operation.LESS_THAN == oper || Operation.NOT_EQUAL == oper);
			}
		}
		else if(fieldType.equals(Boolean.class)) {
			boolean v = getBoolean(fieldName);
			boolean v2 = Boolean.parseBoolean(toValue.toString());
			if(v == v2) {
				return Operation.EQUAL == oper;
			}
			else {
				return Operation.NOT_EQUAL == oper;
			}
		}
		else if(fieldType == String.class) {
			if(value.toString().equals(toValue)) {
				return Operation.EQUAL == oper;
			}
			else if(value.toString().contains(toValue.toString())) {
				return (Operation.INCLUDE == oper || Operation.NOT_EQUAL == oper);
			}
			else if(toValue.toString().contains(value.toString())) {
				return (Operation.IN == oper || Operation.NOT_EQUAL == oper);
			}
			else {
				return (Operation.NOT_EQUAL == oper || Operation.NOT_EQUAL == oper);
			}
		}
		else if(fieldType == Character.class) {
			char v = getChar(fieldName);
			char v2 = toValue.toString().charAt(0);
			if(v == v2) {
				return Operation.EQUAL == oper;
			}
			else if(v > v2){
				return (Operation.GREATER_THAN == oper || Operation.NOT_EQUAL == oper);
			}
			else {
				return (Operation.LESS_THAN == oper || Operation.NOT_EQUAL == oper);
			}
		}
		else if(fieldType == Date.class) {
			Date date = (Date)value;
			Date comparedDate = (Date)toValue;
			if(date.after(comparedDate)) {
				return (Operation.GREATER_THAN == oper || Operation.NOT_EQUAL == oper);
			}
			else if(date.before(comparedDate)) {
				return (Operation.LESS_THAN == oper || Operation.NOT_EQUAL == oper);
			}
			else {
				return (Operation.EQUAL == oper);
			}
		}
		else if(fieldType == Type.class) {
			Type v = getType(fieldName);
			Type v2 = (Type)toValue;
			if(v.equals(v2)) {
				return Operation.EQUAL == oper;
			}
			else {
				return Operation.NOT_EQUAL == oper;
			}
		}
		else if(get(fieldName).equals(toValue)){
			return Operation.EQUAL == oper;
		}
		else {
			return Operation.NOT_EQUAL == oper;
		}
		
	}
	
	@Override
	public String toString() {
		return toXmlString(getEntityName());
	}
	
	public String toXmlString(String tag) {
		HashMap<String, FieldType> fields = getFieldTypes();
		if(m_xmlString == null) {
			m_xmlString = new StringBuilder();
			Set<String> sets = fields.keySet();
			Iterator<String> iterator = sets.iterator();
			while(iterator.hasNext()) {
				String fieldName = iterator.next();
				Object fieldValue = get(fieldName);
				if(fieldValue == null) {
					continue;
				}
				Type fieldType = fields.get(fieldName).getDataType();
				boolean singleData = fields.get(fieldName).isFieldSingle();
				
				String childXmlString = null;
				if(!singleData) {
					ArrayList<?> children = (ArrayList<?>)fieldValue; 
					if(children != null){
						for(int i = 0; i < children.size(); i++) {
							childXmlString = getElementXmlString(fieldName, fieldType, children.get(i));
						}
					}
				}
				else {
					childXmlString = getElementXmlString(fieldName, fieldType, fieldValue);
				}
				m_xmlString.append("\n\t" + childXmlString.replace("\n", "\n\t"));
	
			}
		}
		StringBuilder xmlString = new StringBuilder();
		xmlString.append("<" + tag + ">");
		xmlString.append(m_xmlString.toString());
		xmlString.append("\n" + "</" + getEntityName() + ">\n");
		return xmlString.toString();	
	}
	
	private String getElementXmlString(String tag, Type dataType, Object element) {
		if(Utils.isDerived(Entity.class, (Class<?>)dataType)) {
			Entity entity = (Entity)element;
			String xmlString = entity.toXmlString(tag);
			return xmlString;
		}
		else {
			String value = Utils.getSimpleString(element);
			String xmlString = "<" + tag + ">" + value + "</" + tag + ">";
			return xmlString;
		}
	}
	
	public String getString(String field) {
		return get(field).toString();
	}
	
	public int getInt(String field) {
		return Integer.parseInt(get(field).toString());
	}
	
	public long getLong(String field) {
		return Long.parseLong(get(field).toString());
	}
	
	public float getFloat(String field) {
		return Float.parseFloat(get(field).toString());
	}
	
	public double getDouble(String field) {
		return Double.parseDouble(get(field).toString());
	}
	
	public Date getDate(String field) {
		return (Date)get(field);
	}
	
	public boolean getBoolean(String field) {
		return Boolean.parseBoolean(get(field).toString());
	}
	
	public short getShort(String field) {
		return Short.parseShort(get(field).toString());
	}
	
	public Class<?> getType(String field) {
		return (Class<?>)get(field); 
	}
	
	public char getChar(String field) {
		if(get(field) == null) {
			return 0;
		}
		else {
			return get(field).toString().charAt(0);
		}
	}
}
